package cn.iocoder.yudao.framework.pay.core.client.impl.epayments;

import cn.hutool.core.util.CharsetUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.hash.Hashing;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.TreeMap;
import lombok.extern.slf4j.Slf4j;

/**
 * 签名管理类
 *
 * @author 王广帅
 * @since 2024/1/22 20:58
 */
@Slf4j
public class EPaymentsSignatureManager {

    private static final String SIGN_TYPE = "sign_type";
    public static final String SIGNATURE = "signature";
    private TreeMap<String, String> valueMap;
    private String signValue;

    /**
     * 获取带签名结果的请求参数
     *
     * @param param 请求参数
     * @return 带签名的请求参数
     */
    public String getRequestParam(Object param, EPaymentsClientConfig appConfig) {
        JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(param));
        this.createSignatureValue(jsonObject, appConfig);
        valueMap.put(SIGN_TYPE, "MD5");
        valueMap.put(SIGNATURE, this.signValue);
        return this.getStringValue(true);
    }

    /**
     * 校验签名值,在校验第三方参数时，最好是使用第三方传过来的整体body的转成的json object去验签，防止第三方偶尔添加参数，导致签名失败
     *
     * @param param     需要签名的参数
     * @param signValue 对比的签名值
     * @return 如果签名参数签名之后与对比的签名值一致，返回true
     */
    public boolean checkSign(JSONObject param, String signValue, EPaymentsClientConfig appConfig) {
        this.createSignatureValue(param, appConfig);
        log.info("签名校验：receiveSignValue = {}, newSignValue = {}", signValue, this.signValue);
        return this.signValue.equals(signValue);
    }


    /**
     * 获取签名值
     *
     * @param param 签名参数
     */
    private void createSignatureValue(JSONObject param, EPaymentsClientConfig appConfig) {
        // 签名的时候不带这两个参数
        param.remove(SIGN_TYPE);
        param.remove(SIGNATURE);
        String signContent = this.createContent(param);
        String content = signContent + appConfig.getMerchantKey();
        this.signValue = Hashing.md5().hashString(content, CharsetUtil.CHARSET_UTF_8).toString();
        log.info("生成签名值，content= {}, signValue = {}", content, signValue);
    }

    /**
     * 创建签名值的内容
     *
     * @param param 要签名的参数
     * @return 要签名的内容
     */
    private String createContent(JSONObject param) {
        this.valueMap = new TreeMap<>();
        for (String key : param.keySet()) {
            valueMap.put(key, param.getString(key));
        }
        if (valueMap.isEmpty()) {
            return null;
        }
        return getStringValue(false);
    }

    private String getStringValue(boolean isEncode) {
        StringBuilder strBuild = new StringBuilder();
        valueMap.forEach((k, v) -> {
            if (isEncode) {
                try {
                    v = URLEncoder.encode(v, "utf-8");
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e);
                }
            }
            strBuild.append(k).append("=").append(v).append("&");
        });
        strBuild.delete(strBuild.length() - 1, strBuild.length());
        return strBuild.toString();
    }
}
